<?php
/**
 *         
 * #######################################################################
 *           +----------------------------------------------+
 *           | THIS FILE IS A PART OF "PHP ZONES" FRAMEWORK | 
 *           +----------------------------------------------+
 *           
 *  	THIS CODE IS PROTECTED UNDER Apache Software License
 *  	http://www.apache.org/licenses/LICENSE-2.0
 *
 * 	Simply stating :
 * 		Proprietary Software linking: Yes
 * 		Distribution of this software : Yes
 * 		Redistributing of the code with changes: You can
 * 		Compatible with GNU GPL : NO :D
 *
 * 	Feel free to change and use, not requierd to share the code but appreciated! :)
 *
 * 	www.phpzones.com. mail: mailtoaman@gmail.com
 * #######################################################################
 */


/*************************************************************************
 * 			Initialize
 *************************************************************************/
	// Start the timer
	$zTimeStart = microtime( true );
	
	// Define the preset values
	define('DEFAULT_ZONE_NAME',		'default');
	define('DEFAULT_CONTROLLER_NAME',	'_default');
	define('DEFAULT_THEME_NAME',		'default');
	define('DEFAULT_SESSION_CLASS',	'ZSession');
	define('DEFAULT_APPLET_TRIGGER',	'app');
	define('DEFAULT_LOCALE',			'en');
	define('ZONE_PREFIX',				'__ZONES__');
	define('PZ_FRAMEWORK_DIRECTORY',	realpath(dirname(__FILE__)));
	define('DEFAULT_EXTENSION', 		'.html');
	
	
	// From application index.php file
	global $zAppBaseDir;
	global $zAppConfigFile;
	
	$zZoneBase = $zAppBaseDir . '/zones';
	$zControllerPath = '';
	$zControllerClass = '';
	$zCurrentLocale = DEFAULT_LOCALE; // initialize locale with the preset
	$zDefaultLocale = DEFAULT_LOCALE;
	$zShouldCacheURL = true;
	
	// Suppress all output to use a custom session handler
	ob_start();
	
	//we won't use magic quotes :)
	set_magic_quotes_runtime(0);
	set_magic_quotes_runtime(false);
	ini_set('default_charset','UTF-8');
	
	// The application config file
	if(file_exists($zAppConfigFile)){
		$zConfig = @simplexml_load_file( $zAppConfigFile);
	}else{
		$zErrMsg = "Configuration file not found";
		include 'error.php';
		die();
	}	
	
	// Check if the file loaded successfully
	if(!$zConfig){
		$zErrMsg = "Error loading configuration file";
		include 'error.php';
		die();
	}
	
	// update should cache
	$zShouldCacheURL = zConfig("settings/url_cache");
	if($zShouldCacheURL){
		$zShouldCacheURL = strcmp($zShouldCacheURL,'yes') == 0;
	}	
	
	// Set the class paths
	$zClassPaths =array();
	$zClassPaths[] = $zAppBaseDir . '/classes';
	$zClassPaths[] =  $zAppBaseDir . '/models';
	$zClassPaths[] =  $zAppBaseDir . '/polices';
	

/*************************************************************************
 * 			Load frequently used values
 *************************************************************************/
	
	//get the application folder name in document root
	$zAppDocBase	= substr($_SERVER['PHP_SELF']
							, 0
							, strpos($_SERVER['PHP_SELF'], '/index.php'));
							
	
	//Make the application url
	$zAppBaseURL	= 'http://' . $_SERVER['HTTP_HOST'] . $zAppDocBase;
	

/*************************************************************************
 * 			Setup url cache
 *************************************************************************/
	define('PZ_APP_CACHE_DIR',PZ_FRAMEWORK_DIRECTORY . '/cache'.$zAppDocBase);
	
	$zUrlCache = array();
	$zVarCache = array('__'=>'__');
	$zUrlCacheKey = '';
	$zUrlCacheValue = array();

	if(!is_dir(PZ_APP_CACHE_DIR)){
		mkdir(PZ_APP_CACHE_DIR,0777,true);
		$fh = fopen(PZ_APP_CACHE_DIR.'/urls.pz','w');
		fclose($fh);
		file_put_contents(PZ_APP_CACHE_DIR.'/urls.pz',serialize($zUrlCache));
		
		
		$zDefaultZone = DEFAULT_ZONE_NAME;
		if(zConfig("settings/default_zone")){
			$zDefaultZone = zConfig("settings/default_zone");
	    }
	    $zVarCache['zDefaultZone'] = $zDefaultZone;
	    
	    
	    $zDefaultControllerName = DEFAULT_CONTROLLER_NAME;
		if(zConfig("setttings/default_controller")){
			$zDefaultControllerName = zConfig("settings/default_controller");
	    }
		$zVarCache['zDefaultControllerName'] = $zDefaultControllerName;
		
		$zAppletTrigger = DEFAULT_APPLET_TRIGGER;
		if(zConfig("settings/applet_trigger")){
			$zAppletTrigger = zConfig("settings/applet_trigger");
	    }
		$zVarCache['zAppletTrigger'] = $zAppletTrigger;
		
		
		// Get the session name
		$zVarCache['zSessionClass'] = DEFAULT_SESSION_CLASS;
		$zVarCache['zSessionClassPath'] = PZ_FRAMEWORK_DIRECTORY . '/' .DEFAULT_SESSION_CLASS.'.php';
		if( (zConfig("settings/session_handler"))){
			$zVarCache['zSessionClass'] = zConfig("settings/session_handler");
			$zVarCache['zSessionClassPath'] = $zAppBaseDir.'/classes/'.$zSessionClass.'.php';
			
		}
		
		// set the default locale
		if(zConfig('settings/default_locale')){
			$zDefaultLocale = zConfig('settings/default_locale');
		}
		$zVarCache['zDefaultLocale'] = $zDefaultLocale;
		
		file_put_contents(PZ_APP_CACHE_DIR.'/vars.pz',serialize($zVarCache));
	}
	
	$zUrlCache = unserialize(file_get_contents(PZ_APP_CACHE_DIR.'/urls.pz'));
	$zVarCache = unserialize(file_get_contents(PZ_APP_CACHE_DIR.'/vars.pz'));
	//var_dump($zVarCache);
	
	//Get the default zone name, if not set, zones will take 
    // 'default' as the default zone name.
    $zDefaultZone = $zVarCache['zDefaultZone'];
	$zDefaultControllerName = $zVarCache['zDefaultControllerName'];
	$zAppletTrigger = $zVarCache['zAppletTrigger'];
	$zDefaultLocale = $zVarCache['zDefaultLocale'];
		

/*************************************************************************
 * 			Start the session with the handler
 *************************************************************************/
	zInclude($zVarCache['zSessionClassPath']
					,"Session class file not exists", array('Expected file:'=>$zVarCache['zSessionClassPath'])
				);
	// Instantiate the session
	if(class_exists($zVarCache['zSessionClass'] ,false)){
		$zSession = new $zVarCache['zSessionClass']();
	}else{
		$zErrMsg = "Failed to create session object";
		include 'error.php';
		die();
	}
	
	if(isset($_SESSION['zIsNewSession'])){
		$_SESSION['zIsNewSession'] = false;
	}else{
		$_SESSION['zIsNewSession'] = true;
	}


/*************************************************************************
 *  			Load the theme and set it
 *************************************************************************/
	if(isset($_SESSION[ZONE_PREFIX.'current_theme'])){
		$zCurrentTheme = $_SESSION[ZONE_PREFIX.'current_theme'];
	}else{
		$zCurrentTheme = zConfig("default_theme");
	}

	if( empty( $zCurrentTheme ) ){ 		// Theme not set in config file ?
		$zCurrentTheme = 'default'; 	// Set it default -- convention
	}
	// So not empty, get it from the session
	$_SESSION[ZONE_PREFIX . 'current_theme'] = $zCurrentTheme;



/*************************************************************************
 *  			Process the request URI
 *************************************************************************/
	$zPath= $_SERVER["REQUEST_URI"];	// Get the request URI
	if(strpos($zPath,'?')!==false)
		$zPath = substr($zPath,0,strpos($zPath,'?'));//extract the GET param
   
/**
 * Fix following type of url: www.test.com/hello///home/s////sdf
 * to www.test.com/hello/home/s/sdf
 */
	$zPath = ereg_replace( '/+', "/", $zPath );


/**
 * The url may end with .html extension
 */
	if( substr( $zPath, -(int)(strlen(DEFAULT_EXTENSION)) ) == DEFAULT_EXTENSION ){	// Remove any .html suffix
		$zPath =  substr( $zPath, 0, -(int)(strlen(DEFAULT_EXTENSION)) );
	}
	
	$zCurrentPath = substr( $zPath, mb_strlen( $zAppDocBase, 'UTF-8'  ) );
	
	if( mb_strlen( $zCurrentPath , 'UTF-8' ) > 1 ){	// Eemove slash at front
		$zCurrentPath = substr( $zCurrentPath, 1 );
	}

	if( substr( $zCurrentPath, -1 ) == '/' ){		// Remove any trailing slash
		$zCurrentPath =  substr( $zCurrentPath, 0, -1 );
		
		if(!empty($zCurrentPath)){
			return header('Location:'.$zAppDocBase.'/'.$zCurrentPath.DEFAULT_EXTENSION);
		}
	}

	$zCurrentPath =  ereg_replace( '/+', "/", $zCurrentPath );
	
	if( empty($zCurrentPath)){
		$req = array();
	}else{
		$req = explode( "/", $zCurrentPath );	
	}
	
	$zURLParts = $req;
	
	
	//Include the core library
	zInclude(PZ_FRAMEWORK_DIRECTORY . '/core.php', "Failed to load the core.php!!",array('Expected file:'=>PZ_FRAMEWORK_DIRECTORY . '/core.php') );
	zInclude($zAppBaseDir .'/ssi.php');
		
	// check cache		
	$cache = array();
	if( $zCurrentPath == '' ){
		if( array_key_exists('__pz_index__',$zUrlCache)){
			$cache = $zUrlCache['__pz_index__']; 
		}
	}else{
		foreach($zUrlCache as $aUrl=>$val){
			if( strpos('/'.$zCurrentPath, $aUrl) === 0){
				$cache = $val;
				
			}
		}
	}
	

	if( count($cache) > 0 && 0  ){
		$zCurrentLocale = $cache['locale'];
		$zZoneName = $cache['zone'];
		$zZoneBase = $cache['zZoneBase'];
		array_unshift($zClassPaths
					,$zZoneBase . "/classes"
					,$zZoneBase . "/models"
					,$zZoneBase . "/polices");
		
		//$zControllerPath = $cache['controllerFile'];
		//$zConrolelrClass = $cache['controller'];
		$zMethodName = $cache['method'];
		var_dump($cache);die();
		
		zInclude( $cache['controllerFile'], "Controller file not present!",array('Expected File'=>$cache['controllerFile']));
		$objController = new $cache['controller']();	
		run();
		return;
	}
	
//	echo ('after run.......');
	
	/*************************************************************************
	 *  			Functions related to translation/localization
	 ************************************************************************/
	 			
		if(zConfig('locales/locale')){
			if( !empty($req) && in_array($req[0], zConfig('locales/locale'))){
				$zUrlCacheKey .= '/'.$req[0];
				$zCurrentLocale = array_shift($req); 
			}
		}else{
			if(isset($_SESSION[ZONE_PREFIX.'current_locale'])){ // Not set before in the session?
				$zCurrentLocale = $_SESSION[ZONE_PREFIX.'current_locale']; // 
			}else{
					$zCurrentLocale = $zDefaultLocale;
			}
		}	



	
/*************************************************************************
 *  			Determine if a applet is called
 *************************************************************************/
	
	if( !empty($req) && $zAppletTrigger == $req[0] ){
		array_shift( $req );//remove applet trigger
		
		//Include the core library
		//zInclude(PZ_FRAMEWORK_DIRECTORY . '/core.php', "Failed to load the core.php!!" );
		//zInclude($zAppBaseDir .'/ssi.php');
		
		$appName = array_shift( $req ); // Get the requested component
		
		// check system path
			   
		if(!empty( $appName )){		// If its not empty...
			
			// include the lang file
			global $zCurrentLocale;
			zInclude(zAppletDir($name) . '/lang/'.$zDefaultLocale . '.php');
			zInclude(zAppletDir($name) . '/lang/'.$zCurrentLocale . '.php');
			zInclude(zAppletDir($name) . '/init.php');
			
			$appFun = array_shift( $req ); // Get the function name
			if(empty($appFun)){// no function requested
				$appFun = 'index';
			}
			$appletObj = zApplet($appName);
			if(!method_exists($appletObj,$appFun)){
				array_unshift($req,$appFun);
				$appFun = 'index';
			}
			$zPageTimeStart = microtime(true);
			call_user_func_array(array($appletObj,$appFun),$req);
		
			
		}else{  ///TODO This can be customized later
			return zDie('unknown applet');
			
		}
		//skip rest
		return;
	}

	
/*************************************************************************
 *  			Determine the zone, controller and the method
 *************************************************************************/
 	
 	//Include the core library
	//zInclude( PZ_FRAMEWORK_DIRECTORY .'/core.php',"Failed to load the core.php!" );
	//zInclude($zAppBaseDir .'/ssi.php');

	$zZoneName = $zDefaultZone;
	
	$zControllerName = $zDefaultControllerName;	// The controller name
	$zMethodName = 'index';		// The method name
	
	// Get the zone name
	if( count( $req ) < 1 ){ // If no zone selected, user the default zone
		$zZoneName = $zDefaultZone;
		$zUrlCacheKey = '/';
	}else{
		if( is_dir( zZoneDir($req[0])) ){ // If directory exists then
			$zUrlCacheKey .= '/'. $req[0];
			$zZoneName = array_shift( $req );// get the first part as zone name
			
		}
	}
	
	$zZoneBase = zZoneDir($zZoneName)  ;
	$zUrlCacheValue['zZoneBase'] = $zZoneBase;
	
	// Get the controller name
	if( count( $req ) < 1 ){		// If no controller selected, forward to default index
		$zControllerName = $zDefaultControllerName;	// Set the controller name
		$zMethodName = 'index';				// Set the method name
	}else{	// Load the requested controller and call the method
		if( file_exists( $zZoneBase.'/controllers/'.$req[0] . '.php' ) ){ // If directory exists then
			$zUrlCacheKey .= '/'.$req[0];
			$zControllerName = array_shift( $req );// get the first part as zone name
		}else{
			$zControllerName = $zDefaultControllerName;
		}
	}
	
	// Update the zone classpath;Should search in the current zone first
	array_unshift($zClassPaths
					,$zZoneBase . "/classes"
					,$zZoneBase . "/models"
					,$zZoneBase . "/polices");
	
	// Instantiate the controller	
	//var_dump($zZoneBase);
	zInclude( $zZoneBase . '/controllers/' .$zControllerName .  '.php'
			, "Controller file not present!");
	
	if(class_exists($zControllerName)){
		$objController = new $zControllerName;
	}else{
		zDie("Failed to create controller object");
	}
	
	// Get the method
	if( count( $req ) >0 ){
		if( method_exists($objController,  $req[0]) ){
			$zUrlCacheKey .= '/'.$req[0];
			$zMethodName = array_shift( $req );
		}	
	}
	
	// update cache
	$zUrlCacheValue['locale'] = $zCurrentLocale;
	$zUrlCacheValue['zone'] = $zZoneName;
	$zUrlCacheValue['controller'] = $zControllerName;
	$zUrlCacheValue['controllerFile'] = $zZoneBase.'/controllers/'.$zControllerName . '.php';
	$zUrlCacheValue['method'] = $zMethodName;
	
	if($zUrlCacheKey == '/' || $zUrlCacheKey == '')$zUrlCacheKey= '__pz_index__';
	$zUrlCache[$zUrlCacheKey] = $zUrlCacheValue;
	
	file_put_contents(PZ_FRAMEWORK_DIRECTORY . '/cache'.$zAppDocBase.'/urls.pz',serialize($zUrlCache));
	
	
  
/*************************************************************************
 *  Check permission of the request
 *************************************************************************/
	// If current zone is the default zone, it may not be presetn in the url,
	// so add it infront of the current path and get the polices if any.
	//var_dump($zConfig);die();
	
	function run(){
		global $zCurrentPath;
		global $zZoneName;
		global $objController;
		global $zMethodName;
		global $req;
		global $zCurrentLocale;
		global $zDefaultLocale;
		global $zSession;
		global $zAppBaseDir;
		global $zZoneBase;
		
		// Lastly update the session;
		$_SESSION[ZONE_PREFIX.'current_locale'] = $zCurrentLocale ;
		$_SESSION[ZONE_PREFIX . 'default_locale'] = $zDefaultLocale;
		
		$objController->sessionHandler = $zSession;
		/**
		 * The lang array and try to load the language file. A language
		 * translation file is put under the lang folder.
		 */
			$zLang[]='';
			zInclude($zAppBaseDir . '/lang/'.$zDefaultLocale.'.php');// Try to load the default locale file
			zInclude($zAppBaseDir . '/lang/'.$zCurrentLocale.'.php');// Try to load the locale file
			
		zInclude( $zZoneBase .'/lang/'.$zCurrentLocale.'.php');
		
		// Update the zone base and load if any lang file exists
	$zZoneBase .=  '/' . $zZoneName ;
	zInclude( $zZoneBase .'/lang/'.$zDefaultLocale.'.php');
	zInclude( $zZoneBase .'/lang/'.$zCurrentLocale.'.php');
	
		
		if(strpos($zCurrentPath,$zZoneName)!== 0){
			$polices = zGetPolices($zZoneName. '/' .$zCurrentPath);	
		}else{
			$polices = zGetPolices($zCurrentPath);
		}
		
		
		
		$errorPage	= ''; //TODO : Should be configurable
		$isPermitted = true; // Flag to store permission status
		
		if( empty( $polices ) ){ 						// controller has also no police
			$isPermitted =  true; 						// set permitted
		}else{											// method defined its own police
			//$polices = split( ',', $polices ); 			// take all
			foreach( $polices as $police ){ 			// let all the police to interrogate
				if(empty($police)){
					continue;
				}
				
				if(!class_exists($police)){
					zDie("Police is not defined:" . $police);
				}
				$cop = new $police(); 					// call a police
				if($cop->isOutLaw()){ 					// is outlaw?!
					$isPermitted = false; 				// yea, not permitted
					$errorPage	 = $cop->redirectTo(); 	// get the jail location
					break;
				}		
			}
		}
		
		if( $isPermitted === true ){ // Call the controller method
			if($objController->skipCall()===false){
				try{
					$zPageTimeStart = microtime(true);
					//if( is_callable(array($objController,  $zMethodName)) ){
					//die('---'.$zMethodName);
						call_user_func_array( array( $objController, $zMethodName ), $req );
					//}else{
					//	echo $zMethodName;die(); ///tbd
					//}
					
				}catch(Exception $ex){
					var_dump($ex);
				}
			}
		}else{ // Show the error page, loadview should be used ?
			//zDie("You do not have permission to access this page");
			zRedirect($errorPage,false);
		}
	}

	run();
/*----------------------------------------------------------------------*/

	// simple, right :) ?
	

/**
 * sorry to define this function in engine.php instead of core.php
 * 
 *  Get/Set application configuration data;
 *  
 *  If used with a single parameter, it return takes it as the key and returns
 *  the value.
 *  
 *  If used with double parameter, it sets the second parameter as the value of the key
 *  passed as the first parameter. At present, no xquery cannot be used. You can use only
 *  "/path/to/element"
 *  
 *  Do Not use this function for setting the config.xml unless you are sure;
 *  
 *  If no parameter, it returns the $zConfig array;
 * @param $key 
 */
	
function zConfig($query){
	global $zConfig;
	
	$numArgs = func_num_args();

	switch($numArgs){
		case 0:// no arugment wanted, returns the config object
				return $zConfig;
		case 1: // One argument given, wants the value of a settings
			{
				$query = func_get_arg(0);
				$t = $zConfig->xpath('//config/'.$query);
				if(empty($t)){ // Not found
					return false;
				}
				
				if(count($t)>1){ // Found more than one options, for example locales
					$tmp =array();
					
					foreach($t as $node){ // Push into an array
						$tmp[] = (string)$node;
					}
					return $tmp;				
				}else{	// Found one
					return (string)$t[0];
				}
			}
		case 2:
			{
				$query = func_get_arg(0);
				echo $query;
				$val = func_get_arg(1);
				$nodes = explode('/',$query);
				$key=array_pop($nodes);
				
				foreach($nodes as $node){
					$tmp = $zConfig->{$node};
				}
				$tmp->{$key} = $val;
				global $zAppBaseDir;
				$zConfig->asXML($zAppBaseDir.'/config.xml');
			}
	}
}

function zInclude($file,$zErrMsg=false,$params  = array()){
	
	//echo '<hr/>',$file,'<br>';
	//var_dump($params);
	
	if(!file_exists($file)){// First check if the file exists
		if($zErrMsg!==false){
			zDie($zErrMsg,$params);
		}
	}else{
		if(is_array($params))
		extract($params);
		include $file;
		return true;
	}
	return false;
}
?>